feat(schemadsl): add generic @decorator syntax to the schema DSL - #3265
Draft
josephschorr wants to merge 1 commit into
Draft
feat(schemadsl): add generic @decorator syntax to the schema DSL#3265josephschorr wants to merge 1 commit into
josephschorr wants to merge 1 commit into
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
josephschorr
force-pushed
the
worktree-schema-decorators
branch
from
August 9, 2026 00:11
6584012 to
e046a32
Compare
Adds `@name` and `@name(param: value)` decorators to the schema DSL. A decorator attaches to the declaration that follows it, and is validated against a closed registry that declares, per decorator, its legal sites, its parameters, and the `use` feature flag that enables it. Several decorators may share one flag. Sites: definition, relation, permission, caveat, and individual subject types within a relation's type list. A decorator on a `partial` applies to every definition that includes that partial, collapsing identical duplicates and rejecting conflicting ones. Parameters are always named and separated by `:`. Values are integers, quoted strings, bools, or bare-identifier enum values; the declared parameter type drives coercion, and parameters are stored in the registry's canonical order so generated schema text is stable. This ships the machinery only. No decorator is defined and the production registry is empty, so the feature is inert until one is added; a fixture registry drives the tests. Decorators are stored on the compiled protos, emitted back out by the generator along with the `use` lines they require, and surfaced as deltas by schema diffing. Two pre-existing issues the feature depends on are also fixed: `use` flags are now collected before partials are translated, and translateUseFlag enforces the deployment's allowed-flag set, closing an existing TODO. As a result, a schema declaring a bare `use import` (or `use expiration` where that flag is disabled) now fails to compile where it previously succeeded, since WriteSchema applies DisallowImportFlag unconditionally. Note that the schema DSL has no string escape syntax, so decorator string values are emitted with whichever quote style the value does not contain, and values that cannot be represented are rejected at compile time rather than silently corrupted on regeneration. BREAKING CHANGE: generator.GenerateCaveatSource now returns (string, []string, bool, error). It previously discarded the `use` flags a caveat requires, which would have produced generated schemas that do not recompile.
josephschorr
force-pushed
the
worktree-schema-decorators
branch
from
August 9, 2026 01:16
e046a32 to
4d11d98
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds generic
@decoratorsyntax to the schema DSL. Machinery only — no decorator is defined, and the production registry ships empty, so the feature is inert until one is added.Syntax
A decorator attaches to the declaration that follows it. Sites:
definition,relation,permission,caveat, and individual subject types inside a relation's type list. A decorator on apartialapplies to every definition that includes it — identical duplicates collapse, conflicting ones error.Parameters are always named, separated by
:. Values are integers, quoted strings, bools, or bare-identifier enums; the declared parameter type drives coercion. Parameters are stored in the registry's canonical order so generated schema text is stable.Design
useflags are feature flags; decorators are how a schema applies the feature. The registry maps each decorator to its required flag, N decorators to 1 flag, so there is nouse decoratorsmeta-flag.The parser validates shape only and never consults the registry —
@anything(foo: bar)parses. Names, sites, parameters, and flag gating are all checked by the compiler, which is what lets errors carry source positions.Decorators are stored as a generic
repeated Decoratorfield onNamespaceDefinition,Relation,CaveatDefinition, andAllowedRelation, so a new decorator costs a registry entry and nothing else — no proto change, no generator change.Included fixes
Two pre-existing issues this depends on:
useflags are now collected before partials are translated. PreviouslycollectPartialsran first, so anything inside a partial body validated against an empty flag set.translateUseFlagnow enforces the deployment's allowed-flag set, closing an existingTODO.Behavior changes
use import— with no actualimportstatement — now fails to compile, becauseWriteSchemaappliesDisallowImportFlag()unconditionally. Same shape foruse expirationwhere that flag is disabled. This is the intended effect of enforcing the flag list.generator.GenerateCaveatSourcenow returns(string, []string, bool, error). It previously discarded theuseflags a caveat requires, which would have produced generated schemas that do not recompile.lexer.Flagsandlexer.AllUseFlagsare exported, and a test-onlytestdecoratorsflag is registered in test binaries via atesting.Testing()guard. Downstream consumers (zed, playground) will therefore see it in their test binaries — any golden test asserting theUnknown use flag: … Options are:list will need updating. It cannot reach a production binary.Notes on string values
The DSL has no escape syntax — the lexer scans raw for the closing quote and unquoting is a bare
strings.Trim. Decorator string values are therefore emitted with whichever quote style the value does not contain, and values that cannot be represented (containing both quote styles, or a newline) are rejected at compile time rather than silently corrupted on regeneration. This matters becauseComputeSchemaHashhashes generated source and that hash rides in ZedTokens; round-trip must be a fixed point.Testing
Registry validation, parser fixtures at every site, compiler validation per error case, generator round-trip (including hostile string values), diff deltas, and partial merge semantics. A fixture registry exercises parameter and site combinations no real decorator is expected to have.
Follow-ups
pkg/schema/v2does not carry decorators throughToDefinitions(); dormant today (no callers outside the package) but needed before a core→v2→core round trip can preserve them.